package me.demo.sprite4m;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import me.demo.sprite4m.action.Action;
import me.demo.sprite4m.behavior.Behavior;
import me.demo.sprite4m.environment.MascotEnvironment;
import me.demo.sprite4m.exception.CantBeAliveException;
import me.demo.sprite4m.image.MascotImage;
import me.demo.sprite4m.image.TranslucentWindow;

/**
 * 精灵
 *
 * 长期复杂的行为 {@link Behavior}
 * 短期单调的运动{@link Action}
 *
 * 精灵内部有计时器,每隔一定间隔调用{@link Action}  {@link #animate(Point, MascotImage, boolean)}
 * 让精灵动起来
 *
 * {@link Action} 结束后以其他特定行为 {@link Behavior} 触发其他动作 {@link Action}.
 *
 */
public class Mascot {

	private static final long serialVersionUID = 1L;

	private static final Logger log = Logger.getLogger(Mascot.class.getName());

	/**
	 * 最后生成的精灵ID.
	 */
	private static AtomicInteger lastId = new AtomicInteger();

	/**
	 * 精灵ID
	 * 只是为了更容易看到调试用的日志而存在.
	 */
	private final int id;

	/**
	 * 显示精灵窗口
	 */
	private final TranslucentWindow window = NativeFactory.getInstance().newTransparentWindow();

	/**
	 * マスコットを管理しているマネージャ.
	 */
	private Manager manager = null;

	/**
	 * マスコットの接地座標.
	 * たとえば足元や、ぶら下がっているときの手の部分など.
	 * ここが画像を表示するときの中心になる.
	 */
	private Point anchor = new Point(0, 0);

	/**
	 * 表示する画像.
	 */
	private MascotImage image = null;

	/**
	 * 右向きかどうか.
	 * オリジナル画像は左向きとして扱われるので、trueを設定すると反転して描画される.
	 */
	private boolean lookRight = false;

	/**
	 * 長期的な振る舞いをあらわすオブジェクト.
	 */
	private Behavior behavior = null;

	/**
	 * タイマーの1チックごとに増加する時刻.
	 */
	private int time = 0;

	/**
	 * アニメーション実行中かどうか.
	 */
	private boolean animating = true;

	/**
	 * マスコットの表示環境.
	 */
	private MascotEnvironment environment = new MascotEnvironment(this);

	public Mascot() {
		this.id = lastId.incrementAndGet();

		log.log(Level.INFO, "マスコット生成({0})", this);

		// 常に最善面に表示
		getWindow().asJWindow().setAlwaysOnTop(true);

		// マウスハンドラを登録
		getWindow().asJWindow().addMouseListener(new MascotEventHandler(this));

	}

	@Override
	public String toString() {
		return "マスコット" + this.id;
	}

	void tick() {
		if (isAnimating()) {
			if (getBehavior() != null) {

				try {
					getBehavior().next();
				} catch (final CantBeAliveException e) {
					log.log(Level.SEVERE, "生き続けることが出来ない状況", e);
					dispose();
				}

				setTime(getTime() + 1);
			}
		}
	}

	public void apply() {
		if (isAnimating()) {

			// 表示できる画像が無ければ何も出来ない
			if (getImage() != null) {

				// ウィンドウの領域を設定
				getWindow().asJWindow().setBounds(getBounds());

				// 画像を設定
				getWindow().setImage(getImage().getImage());

				// 表示
				if (!getWindow().asJWindow().isVisible()) {
					getWindow().asJWindow().setVisible(true);
				}

				// 再描画
				getWindow().updateImage();
			} else {
				if (getWindow().asJWindow().isVisible()) {
					getWindow().asJWindow().setVisible(false);
				}
			}
		}
	}

	public void dispose() {
		log.log(Level.INFO, "マスコット破棄({0})", this);

		getWindow().asJWindow().dispose();
		if (getManager() != null) {
			getManager().remove(Mascot.this);
		}
	}

	public Manager getManager() {
		return this.manager;
	}

	public void setManager(final Manager manager) {
		this.manager = manager;
	}

	public Point getAnchor() {
		return this.anchor;
	}

	public void setAnchor(final Point anchor) {
		this.anchor = anchor;
	}

	public MascotImage getImage() {
		return this.image;
	}

	public void setImage(final MascotImage image) {
		this.image = image;
	}

	public boolean isLookRight() {
		return this.lookRight;
	}

	public void setLookRight(final boolean lookRight) {
		this.lookRight = lookRight;
	}

	public Rectangle getBounds() {

		// 接地座標と画像の中心座標からウィンドウの領域を求める.
		final int top = getAnchor().y - getImage().getCenter().y;
		final int left = getAnchor().x - getImage().getCenter().x;

		final Rectangle result = new Rectangle(left, top, getImage().getSize().width, getImage().getSize().height);

		return result;
	}

	public int getTime() {
		return this.time;
	}

	private void setTime(final int time) {
		this.time = time;
	}

	public Behavior getBehavior() {
		return this.behavior;
	}

	public void setBehavior(final Behavior behavior) throws CantBeAliveException {
		this.behavior = behavior;
		this.behavior.init(this);
	}

	public int getTotalCount() {
		return getManager().getCount();
	}

	private boolean isAnimating() {
		return this.animating;
	}

	void setAnimating(final boolean animating) {
		this.animating = animating;
	}

	TranslucentWindow getWindow() {
		return this.window;
	}

	public MascotEnvironment getEnvironment() {
		return environment;
	}
}
